package org.kernely.persistence;

import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.osgi.framework.Bundle;

/**
 * The dynamic annotation configuration to add and remove classes
 */
public class DynamicConfigurationHolder {

	/**
	 * The serialization id
	 */
	private static final long serialVersionUID = 9087607678908827148L;

	// the annotated class list
	private List<Class<?>> annotatedClassList;

	// the url hibernate configuration file
	private URL configurationFile;

	/**
	 * Entities' package header in the Bundle's Manifest
	 */
	private static final String HIBERNATE_PACKAGE_HEADER_KEY = "Hibernate-package";


	/**
	 * Entities' package name in the bundle's Manifest
	 */
	private String packageName;

	/**
	 * DynamicConfigurationHolder's constructor
	 */
	public DynamicConfigurationHolder() {
		annotatedClassList = new ArrayList<Class<?>>();
	}

	/**
	 * Add an annotated class
	 * 
	 * @param pClass
	 */
	public final void addAnnotatedClass(Class<?> pClass) {
		annotatedClassList.add(pClass);

	}

	/**
	 * Removes an annotated class from the list
	 * 
	 * @param pClass
	 *            the class to removes from the list
	 */
	public final void removeAnnotatedClass(Class<?> pClass) {
		annotatedClassList.remove(pClass);
	}

	/**
	 * Return all the annotated classes in the bundle
	 * 
	 * @param pBundle
	 * @return
	 */
	public final List<Class<?>> getAnnotatedClass(Bundle pBundle) {
		List<Class<?>> classes = new ArrayList<Class<?>>();
		// Retrieve entities' package name
		String hibernatePackage = (String) pBundle.getHeaders().get(HIBERNATE_PACKAGE_HEADER_KEY);
		if (hibernatePackage != null && !hibernatePackage.isEmpty()) {
			try {
				// Replace package name by path
				String path = hibernatePackage.replace('.', '/');
				// Retrieve the resources located at the specified path
				if(pBundle.getState() > Bundle.INSTALLED){
					URL resource = pBundle.getResource(path);
					
					if (resource == null) {
						throw new ClassNotFoundException("No resource for " + path);
					}

					if (resource.getProtocol().equals("bundle")) {
						// Retrieve all entries corresponding to the path
						Enumeration ep = pBundle.getEntryPaths(path);
						while (ep.hasMoreElements()) {
							String s = ep.nextElement().toString();
							String tmp = s.substring(0, s.indexOf('.'));
							String realPath = tmp.replace('/', '.');
							// remove entity from annotated entities list
							Class<?> loadedClass = pBundle.loadClass(realPath);
							classes.add(loadedClass);
						}
					}
				}
				

			} catch (Exception e) {

				e.printStackTrace();
			}
		}

		return classes;
	}

	/**
	 * The method refresh the hibernate factory
	 * 
	 * @return the refresh hibernate factory
	 */
	public final SessionFactory refreshFactory() {

		AnnotationConfiguration cfg = new AnnotationConfiguration();
		cfg.configure(configurationFile);

		// the annotated class list
		for (Class<?> clazz : annotatedClassList) {
			cfg.addAnnotatedClass(clazz);
		}
		return cfg.buildSessionFactory();
	}

	/**
	 * The method set the URL session factory
	 * 
	 * @param pHibernateConfigurationFile
	 */
	public final void setConfigurationFile(URL pHibernateConfigurationFile) {
		this.configurationFile = pHibernateConfigurationFile;
	}

	/**
	 * Removes all annotated classes to the configuraiton
	 * 
	 * @param classes
	 *            the list of classes to remove
	 */
	public final void removeAnnotatedClasses(List<Class<?>> classes) {
		annotatedClassList.removeAll(classes);
	}

	/**
	 * Adds all annotated classes to the configuraiton
	 * 
	 * @param classes
	 *            the list of classes to add
	 */
	public final void addAnnotatedClasses(List<Class<?>> classes) {
		annotatedClassList.addAll(classes);
	}

	public final void setAnnotatedClassList(List<Class<?>> annotatedClassList) {
		this.annotatedClassList = annotatedClassList;
	}

}